﻿using System;
using System.Collections.Generic;
namespace Bouyei.GeoCore.Geometries
{
    public class GeoPolygon : Geometry
    {
        public GeoPolygon(GeoSequence[] sequences)
            :base(GeoType.POLYGON,sequences)
        { }

        public GeoPolygon(string wktString)
            :base(GeoType.POLYGON,wktString)
        { }

        public GeoPolygon(Coordinate[] coords)
            : base(GeoType.POLYGON,coords)
        {
            if(GetSequence(0,0).IsRing()==false)
            {
                GetSequence(0,0).Add(coords[0]);
            }
        }

        public GeoPolygon(Coordinate[] outer,List<Coordinate[]> interior)
            :base(outer,interior)
        {
            for (int i = 0; i < GeometryCount; ++i)
            {
                if (GetSequence(0,i).IsRing() == false)
                {
                    if (i == 0) GetSequence(0,0).Add(outer[0]);
                    else GetSequence(0,i).Add(interior[i][0]);
                }
            }
        }

        public GeoPolygon(GeoPoint[] geoPoints) 
            : base(GeoType.POLYGON,geoPoints)
        {
            if (GetSequence(0, 0).IsRing() == false)
            {
                GetSequence(0, 0).Add(geoPoints[0]);
            }
        }

        public GeoPolygon(GeoLinestring linestring)
            : base(GeoType.POLYGON,linestring)
        {
            if (GetSequence(0, 0).IsRing() == false)
            {
                GetSequence(0, 0).Add(linestring[0]);
            }
        }

        public bool Intersects(GeoPolygon polygon)
        {
            var outerbox = polygon.GetBoundrayBox(0);
            var tsOuterbox = GetBoundrayBox(0);

            bool hasIntersect = IsIntersects(outerbox.Start.ToCoordinate(), outerbox.End.ToCoordinate(),
                tsOuterbox.Start.ToCoordinate(), tsOuterbox.End.ToCoordinate());

            if (hasIntersect == false) return false;

            var outerSeq = polygon.GetSequence(0, 0);
            var tsOuterSeq = GetSequence(0, 0);

            return true;
        }

        public GeoLineSegment GetBoundrayBox(int index)
        {
            var seq = GetSequence(0, index);
            int len = seq.Count;

            Coordinate min = seq[0].Copy(), max = seq[0].Copy();

            for (int i = 1; i < len; ++i)
            {
                min.X = min.MinX(seq[i]);
                min.Y = min.MinY(seq[i]);

                max.X = max.MaxX(seq[i]);
                max.Y = max.MaxY(seq[i]);
            }
            return new GeoLineSegment (min, max);
        }

        public bool Contains(Coordinate coord)
        {
            //外环
            bool isContained = Contains(coord, GetSequence(0,0));
            if (isContained == false) return isContained;

            bool isInterior = false;

            //内环
            for (int i = 1; i < GeometryCount; ++i)
            {
                isInterior = Contains(coord, GetSequence(0, i));
                if (isInterior) break;
            }
            return isContained && (isInterior == false);
        }

        private bool Contains(Coordinate coord,GeoSequence sequence)
        {
            int cnt = sequence.Count - 1;
            bool isContained = false;
         
            for (int i = 0, j = cnt - 1; i < cnt; ++i)
            {
                var p1 = sequence[i];
                var p2 = sequence[j];

                if (p1.Y < coord.Y && p2.Y >= coord.Y || p2.Y < coord.Y && p1.Y >= coord.Y)
                {
                    if (p1.X + (coord.Y - p1.Y) / (p2.Y - p1.Y) * (p2.X - p1.X) < coord.X)
                    {
                        isContained = !isContained;
                    }
                }
                j = i;
            }
            return isContained;
        }
    }
}
